#ifndef DYNAMICCLASS_H
#define DYNAMICCLASS_H

#include <boost/any.hpp>

#include <map>
#include <iostream>

#include <utils/String.h>

// Purpose: Provide dynamically-typed variables and a dynamic class type
// Status:  Very usable, but still needs some additions

//TODO: Use virtual base class functions to encode for networking

//TODO: Consider making a 'blueprint' version of DynamicClass that has only an
//      'add' member and no actual values, only types.

//TODO: Re-check why (if) we need to store DynamicVar*s in the map rather than
//      letting it do all of the dynamic allocation.

//TODO: Think about how to do typeid --> TypedDynamicVar< type >
//      Could have a singleton type manager that contains a
//      map<typeid[.name()], DynamicVar*> and uses the clone() method to create
//      typed instances. The map could be filled using the self-registration
//      technique.

template< class T >
class TypedDynamicVar;

// boost::any takes care of holding and copying 'anything', while a set of
//  virtual functions provide type-remembering operations.
class DynamicVar
{
	//Our 'anything' data member
	boost::any m_Data;
	
	bool m_bIsReferenceOnly;
	
	//This is an abstract base class
	DynamicVar() {}
	//Assign data
	//TODO: Check whether this should be by reference or value
	//TODO: Check whether this should exist at all, or at least whether it
	//      should have some type checking.
	//No assignment, only construction from data and copying
	/*template< typename T >
	T& operator = ( T& value )
	{
		m_Data = value;
		return value;
	}*/
	
	//TODO: Assignment operator and copy constructor!
	//      Actually, they are probably handled ok by boost::any.
	
protected:
	//NOTE: We could just ask for a boost::any here, but I think it's nicer to
	//      hide the underlying 'anything' storage technique.
	template< typename T >
	DynamicVar( T& data, bool bIsReferenceOnly=false )
	 : m_Data( data ), m_bIsReferenceOnly( bIsReferenceOnly ) {}
	//Copy constructor should just work as it will call that of boost::any
public:
	
	//Polymorphic type-remembering operations
	//---------------------------------------
	//Allows us to copy the type information from only a base pointer
	virtual DynamicVar* clone() const = 0;
	
	//TODO: See if this double-dispatch works
	virtual bool operator== ( const DynamicVar& otherVar ) const = 0;
	/*{
		if ( this->getType() != otherVar.getType() )
			return false;
		return otherVar.operator== ( *this );
	}*/
	virtual bool operator!= ( const DynamicVar& otherVar ) const = 0;
	/*{
		if ( this->getType() != otherVar.getType() )
			return true;
		return otherVar.operator!= ( *this );
	}*/
	
protected:
	virtual void write( std::ostream& osStream ) const = 0;
public:
	//---------------------------------------
	
	friend std::ostream& operator << ( std::ostream& os, const DynamicVar& obj );
	
	const std::type_info& getType() const { return m_Data.type(); }
	
	template< typename T >
	bool isSameTypeAs( const T& value ) const { return m_Data.type() == typeid(T); }
	
	/*bool operator== ( const DynamicVar& otherVar )
	{
		return ( this->m_Data.type() == otherVar.m_Data.type() &&
		         this->m_Data );
	}
	bool operator!= ( const DynamicVar& otherVar )
	{
		return ( this->m_Data != otherVar.m_Data );
	}*/
	
	//bool isReferenceOnly() { return m_bIsReferenceOnly; }
	
	//Get data by value
	template< typename T >
	T getData() const
	{
		T val;
		
		if ( m_bIsReferenceOnly )
			val = *boost::any_cast<T*>( m_Data );
		else
			val = boost::any_cast<T>( m_Data );
		
		return val;
	}
	
	//Get data by reference
	template< typename T >
	T& getData()
	{
		T* pData;
		
		if ( m_bIsReferenceOnly ) {
			pData = *boost::any_cast<T*>( &m_Data );
			if ( !pData ) {
				std::cout << "Error in (reference-only) DynamicVar::getData<"
				          << typeid(T*).name()
				          << ">(): This variable is of type `" << getType().name()
				          << "'." << std::endl;
				//TODO: Throw an exception here.
			}
		}
		else {
			pData = boost::any_cast<T>( &m_Data );
			if ( !pData ) {
				std::cout << "Error in DynamicVar::getData<" << typeid(T).name()
				          << ">(): This variable is of type `" << getType().name()
				          << "'." << std::endl;
				//TODO: Throw an exception here.
			}
		}
		
		return *pData;
		
		/*
		T* pData = boost::any_cast<T>( &m_Data );
		if ( !pData ) {
			std::cout << "Error in DynamicVar::getData<" << typeid(T).name()
			          << ">(): This variable is of type `" << getType().name()
			          << "'." << std::endl;
			//TODO: Throw an exception here.
		}
		
		//Return a reference to the data
		return *pData;
		*/
	}
};

std::ostream& operator << ( std::ostream& os, const DynamicVar& obj )
{
	obj.write( os );
	return os;
}

//TODO: See how extensible this can be made
template< typename T >
class TypedDynamicVar : public DynamicVar
{
public:
	
	TypedDynamicVar( T data, bool bIsReferenceOnly=false ) : DynamicVar( data, bIsReferenceOnly ) {}
	
	//operator = ( T& value
	
	//Allows us to copy the type information from only a base pointer
	virtual DynamicVar* clone() const
	{
		return new TypedDynamicVar<T>( *this );
	}
	
	//TODO: See if this double-dispatch works
	//TODO: Use the type knowledge to cast when needed
	virtual bool operator== ( const DynamicVar& otherVar ) const
	{
		if ( this->getType() != otherVar.getType() )
			return false;
		return ( this->getData<T>() == otherVar.getData<T>() );
	}
	virtual bool operator!= ( const DynamicVar& otherVar ) const
	{
		if ( this->getType() != otherVar.getType() )
			return true;
		return ( this->getData<T>() != otherVar.getData<T>() );
	}
	
protected:
	
	//Write self to an ostream so that stream operators will work
	virtual void write( std::ostream& osStream ) const
	{
		osStream << this->getData<T>();
	}
};
/*
class DynamicMethod
{
	
public:
	
	virtual void call( DynamicVar& arg1, DynamicVar& arg2, DynamicVar& arg3,
	                   DynamicVar& arg4, DynamicVar& arg5, DynamicVar& arg6 )
	{
		
	}
	
};
*/
class DynamicClass
{
	
	typedef std::map<String, DynamicVar*> VarMap;
	typedef std::map<String, DynamicVar*>::iterator VarMapIterator;
	
	//Dynamic String-keyed map of member variables
	VarMap m_VarMap;
	//One string defining the particular 'type' of this dynamic class
	String m_sType;
	
	//Must not call constructor without type string
	DynamicClass() {}
	
	DynamicVar* _findVar( String sName )
	{
		//Look up the variable in the map
		VarMapIterator itFoundVar = m_VarMap.find( sName );
		//Check that it was found
        if (itFoundVar == m_VarMap.end())
    		return NULL;
    	
    	//Return the pointer to the DynamicVar
    	return itFoundVar->second;
	}
	
	const DynamicVar* _findVar( String sName ) const
	{
		//Look up the variable in the map
		std::map<String, DynamicVar*>::const_iterator itFoundVar = m_VarMap.find( sName );
		//Check that it was found
        if (itFoundVar == m_VarMap.end())
    		return NULL;
    	
    	//Return the pointer to the DynamicVar
    	return itFoundVar->second;
	}
	
public:
	
	//Normal constructor
	DynamicClass(String sType) : m_sType(sType) 		{}
	
	//TODO: Work out how m_sType should be copied
	
	//Copy constructor
	DynamicClass( const DynamicClass& sourceClass )
	{
		//Copy and allocate memory for each variable in sourceClass
		std::map<String, DynamicVar*>::const_iterator itSourceVar;
		for (itSourceVar = sourceClass.m_VarMap.begin();
		     itSourceVar != sourceClass.m_VarMap.end();
			 ++itSourceVar) {
            //Call the polymorphic clone() function to copy type information
            m_VarMap[ itSourceVar->first ] = itSourceVar->second->clone();
		}
		
		//And also copy the type of the source class
		m_sType = sourceClass.getType();
    }
	
	//Assignment operator
	DynamicClass& operator= ( const DynamicClass& sourceClass )
	{
		//Make sure that the DynamicClass types match
		if ( m_sType != sourceClass.getType() ) {
			std::cerr << "Error in DynamicClass assignment: Cannot assign a \
DynamicClass of type `"
			          << sourceClass.getType()
			          << "' to a DynamicClass of type `"
			          << m_sType
			          << "'. Operation failed." << std::endl;
			return *this;
		}
		
		//Create a temporary variable map to hold the new variables
		VarMap tmpVarMap;
		
		//Copy and allocate memory for each variable in sourceClass
		std::map<String, DynamicVar*>::const_iterator itSourceVar;
		for (itSourceVar = sourceClass.m_VarMap.begin();
		     itSourceVar != sourceClass.m_VarMap.end();
			 ++itSourceVar) {
            //Call the polymorphic clone() function to copy type information
            tmpVarMap[ itSourceVar->first ] = itSourceVar->second->clone();
		}
		
		//Then delete each variable in our map
		VarMapIterator itVar;
		for (itVar = m_VarMap.begin(); itVar != m_VarMap.end(); ++itVar)
            delete itVar->second;
		
		//And finally copy the temporary map to our map
		m_VarMap = tmpVarMap;
		
		return *this;
	}
	
	//Destructor
    virtual ~DynamicClass()
    {
		//Delete each variable in the map
		VarMapIterator itVar;
		for (itVar = m_VarMap.begin(); itVar != m_VarMap.end(); ++itVar)
            delete itVar->second;
	}
	
	//Comparison operators
	bool operator== ( const DynamicClass& otherClass ) const
	{
		//First, their types must match
		if ( this->m_sType != otherClass.m_sType )
			return false;
		//Second, the number of dynamic variables must match
		if ( this->m_VarMap.size() != otherClass.m_VarMap.size() )
			return false;
		//Finally, the names and values of each dynamic variable must match
		//std::map<String, DynamicVar*>::const_iterator itThisVar;
		std::map<String, DynamicVar*>::const_iterator itOtherVar;
		/*for (itOtherVar=otherClass.m_VarMap.begin(), itThisVar=m_VarMap.begin();
		     itOtherVar!=otherClass.m_VarMap.end(), itThisVar!=m_VarMap.end();
		     ++itOtherVar, ++itThisVar)*/
		for (itOtherVar=otherClass.m_VarMap.begin();
		     itOtherVar!=otherClass.m_VarMap.end();
		     ++itOtherVar) {
			/*if ( itThisVar->first != itOtherVar->first ||
			     *itThisVar->second != *itOtherVar->second )
				return false;*/
			
			//Look for the same variable in this class
			const DynamicVar* pFoundVar = this->_findVar( itOtherVar->first );
			if ( !pFoundVar || *pFoundVar != *itOtherVar->second )
				return false;
		}
			
		//They must be the same
		return true;
	}
	
	//Comparison operators
	bool operator!= ( const DynamicClass& otherClass ) const
	{
		return !this->operator==( otherClass );
	}
	
	//Once the class is constructed, the 'type' member is read-only
	String getType() const				{ return m_sType; }
	/*
	class NULLType {};
	
	template< class T_ReturnType, class T_Arg1Type, class T_Arg2Type=NULLType,
	          class T_Arg3Type=NULLType, class T_Arg4Type=NULLType,
			  class T_Arg5Type=NULLType, class T_Arg6Type=NULLType >
	bool addAction( String sName, int iArgCount )
	{
		
	}
	*/
	
	/*class PhysicsEntity : public Entity, public IDrawable
	{
		m_PhysicsPoint* m_pPoint;
	public:
		PhysicsEntity()
		{
			addRealVar( "pPoint", m_pPoint ) = new PhysicsPoint;
		}
	};
	*/
	
	//TODO: Clean-up the error outputs in this file.
	
	//Use this to allow hard-coded member variables to be accessed as if they
	// were dynamic variables.
	template< class T >
	T& addRealVar( String sName, T& value )
	{
		//Check if a variable with this name already exists
		if (m_VarMap.find(sName) != m_VarMap.end()) {
    		std::cerr << "Error in DynamicClass::addVar: Can't add new \
variable, dynamic class of type `"
			          << m_sType
					  << "' already contains a member variable with the name `"
					  << sName
					  << "'" << std::endl;
    		//TODO: Throw exception here!
    	}
    	
		//Print out information (temporary)
		std::cout << "Added (reference-only) member variable `" << sName
    				<< "' of type `" << typeid(T).name()
    				<< "' to dynamic class of type `" << m_sType
    				<< "'" << std::endl;
    	
		m_VarMap[ sName ] = new TypedDynamicVar<T*>( &value, true );
		return value;
	}
	
	//Add a typed variable
	template< class T >
	bool addVar( String sName, T value )
	{
		//Check if a variable with this name already exists
		if (m_VarMap.find(sName) != m_VarMap.end()) {
    		std::cerr << "Error in DynamicClass::addVar: Can't add new \
variable, dynamic class of type `"
			          << m_sType
					  << "' already contains a member variable with the name `"
					  << sName
					  << "'" << std::endl;
    		return false;
    	}
    	
		//Print out information (temporary)
		std::cout << "Added member variable `" << sName
    				<< "' of type `" << typeid(T).name()
    				<< "' to dynamic class of type `" << m_sType
    				<< "'" << std::endl;
		
		//Use assignment operator of DynamicVar to assign the value
		//m_VarMap[ sName ] = value;
		
		m_VarMap[ sName ] = new TypedDynamicVar<T>( value );
		return true;
	}
	
	//Add a typed variable (alternate syntax)
	template< class T >
	T& addVar( String sName )
	{
		//NOTE: This addVar call here should 'take care of' any errors
		addVar( sName, T() );
		DynamicVar& var = *_findVar( sName );
		return var.getData<T>();
	}
	
	//Typed variable access
	template< class T >
	T& getVar( String sName )
	{
		//Look up the variable in the map
		DynamicVar* pVar = _findVar( sName );
		if ( !pVar ) {
			std::cerr << "Error in DynamicClass::getVar<"
			          << typeid(T).name()
			          << ">(): There is no variable named `"
                      << sName
    		          << "' within dynamic class `"
    		          << m_sType << "'" << std::endl;
    		//TODO: Throw an exception here!
    		//throw "getVar failure"; ??
		}
		
		//Get a nice reference to the found variable
    	DynamicVar& var = *pVar;
    	/*
    	//Check that the existing variable with this name matches the given type
    	if ( var.getType() != typeid(T) ) {
			std::cerr << "Error in DynamicClass: Request for getVar<"
			          << typeid(T).name()
					  << ">( \""
					  << sName
					  << "\" ) does not match the the variable's type, which \
is `"
			          << var.getType().name()
					  << "'. Bailing." << std::endl;
			//TODO: Throw an exception here!
		}
    	*/
    	//Return a reference to the safely-cast data
    	return var.getData<T>();
	}
	/*
	template< class T >
	TypedDynamicVar<T>& getVar( String sName )
	{
		//Look up the variable in the map
		VarMapIterator itFoundVar = m_VarMap.find( sName );
		//Check that it was found
        if (itFoundVar == m_VarMap.end()) {
    		std::cerr << "Error in DynamicClass: There is no variable named `"
                        << sName
    					<< "' within dynamic class `"
    					<< m_sType << "'" << std::endl;
    		//NOTE: This is really bad, but there's no choice when we want to
    		//      use references. And it should probably be a fatal error
    		//      anyway.
    		//TODO: Probably should throw an exception.
    		return *((TypedDynamicVar<T>*)0);
    	}
    	
    	//Get a nice reference to the variable we are dealing with
    	DynamicVar& var = *itFoundVar->second;
    	
    	//Check that the type of the variable matches that of value
    	T value;
    	if ( !var.isSameTypeAs(value) ) {
			std::cerr << "Error in DynamicClass::getVar: Trying to assign \
value of type `" << typeid(T).name() << "' to dynamic variable `" << sName << \
"' which is of type `" << var.getType().name() << "'. Bailing." << std::endl;
			return *((TypedDynamicVar<T>*)0);
		}
    	
    	//It is now safe to up-cast
    	return static_cast< TypedDynamicVar<T>& >( var );
	}
	*/
	
	//Un-typed variable access
	DynamicVar& getVar( String sName )
	{
		//Look up the variable in the map
		DynamicVar* pVar = _findVar( sName );
		if ( !pVar ) {
			std::cerr << "Error in DynamicClass::getVar(): There is no \
variable named `"
                      << sName
    		          << "' within dynamic class `"
    		          << m_sType << "'" << std::endl;
    		//TODO: Throw an exception here!
		}
		
		//Return a reference to the un-typed DynamicVar
		return *pVar;
	}
	
	template< class T >
	bool setVar( String sName, T value )
	{
		//Look up the variable in the map
		DynamicVar* pVar = _findVar( sName );
		if ( !pVar ) {
			std::cerr << "Error in DynamicClass::setVar<"
			          << typeid(T).name()
			          << ">( ... ): There is no variable named `"
                      << sName
    		          << "' within dynamic class `"
    		          << m_sType << "'" << std::endl;
    		return false;
		}
		
		//Get a nice reference to the found variable
    	DynamicVar& var = *pVar;
    	
    	//Check that the type of the variable matches that of value
    	if ( !var.isSameTypeAs(value) ) {
			std::cerr << "Error in DynamicClass::setVar< ... >( ... ): Trying \
to assign value of type `"
			          << typeid(T).name()
					  << "' to dynamic variable `"
                      << sName
					  << "' which is of type `"
					  << var.getType().name()
					  << "'. Bailing." << std::endl;
			return false;
		}
    	
    	//Assign a dynamically-typed version of the value
    	//TODO: Just check this
    	var = TypedDynamicVar<T>( value );
    	
    	return true;
	}
	
};

#endif
